﻿using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;

namespace Shc.Json
{
    
    /// <summary>
    /// Json base data struct, it can convert to any C# system base type.
    /// </summary>
    public class JsonObject
    {
        private object m_obj;

        public object BaseObj
        {
            get { return m_obj; }
        }

        private JsonObject(object obj)
        {
            m_obj = obj;
        }

        public override string ToString()
        {
            return this;
        }

        public JsonObject()
        {
            m_obj = null;
        }

        #region Constructors
        public static implicit operator JsonObject(bool value) { return new JsonObject(value); }
        public static implicit operator JsonObject(byte value) { return new JsonObject(value); }
        public static implicit operator JsonObject(char value) { return new JsonObject(value); }
        public static implicit operator JsonObject(DateTime value) { return new JsonObject(value); }
        public static implicit operator JsonObject(decimal value) { return new JsonObject(value); }
        public static implicit operator JsonObject(double value) { return new JsonObject(value); }
        public static implicit operator JsonObject(short value) { return new JsonObject(value); }
        public static implicit operator JsonObject(int value) { return new JsonObject(value); }
        public static implicit operator JsonObject(long value) { return new JsonObject(value); }
        public static implicit operator JsonObject(sbyte value) { return new JsonObject(value); }
        public static implicit operator JsonObject(float value) { return new JsonObject(value); }
        public static implicit operator JsonObject(string value) { return new JsonObject(value); }
        public static implicit operator JsonObject(ushort value) { return new JsonObject(value); }
        public static implicit operator JsonObject(uint value) { return new JsonObject(value); }
        public static implicit operator JsonObject(ulong value) { return new JsonObject(value); }
        public static implicit operator JsonObject(JsonHashTable value) { return new JsonObject(value); }
        public static implicit operator JsonObject(JsonList value) { return new JsonObject(value); }

        public static implicit operator JsonObject(bool? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(byte? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(char? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(DateTime? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(decimal? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(double? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(short? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(int? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(long? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(sbyte? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(float? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(ushort? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(uint? value) { return new JsonObject(value); }
        public static implicit operator JsonObject(ulong? value) { return new JsonObject(value); }
        #endregion

        #region Data type
        public bool IsBool() { return m_obj is bool; }
        public bool IsByte() { return m_obj is byte; }
        public bool IsChar() { return m_obj is char; }
        public bool IsDateTime() { return m_obj is DateTime; }
        public bool IsDecimal() { return m_obj is decimal; }
        public bool IsDouble() { return m_obj is double; }
        public bool IsShort() { return m_obj is short; }
        public bool IsInt() { return m_obj is int; }
        public bool IsLong() { return m_obj is long; }
        public bool IsSByte() { return m_obj is sbyte; }
        public bool IsFloat() { return m_obj is float; }
        public bool IsString() { return m_obj is string; }
        public bool IsUShort() { return m_obj is ushort; }
        public bool IsUInt() { return m_obj is uint; }
        public bool IsULong() { return m_obj is ulong; }
        public bool IsHashTable() { return m_obj is JsonHashTable; }
        public bool IsArrayList() { return m_obj is JsonList; }
        public bool IsNull() { return m_obj == null; }
        #endregion

        #region Convert to Simple Data

        /// <summary>
        /// Convert to bool
        /// </summary>
        /// <param name="obj">json obj(string "true" or !0 return true, others return false)</param>
        /// <returns>bool</returns>
        public static implicit operator bool(JsonObject obj)
        {
            bool res = false;
            if (obj == null || obj.m_obj == null)
                return res;
            return bool.TryParse(obj.m_obj.ToString(), out res) ? res : (int)obj != 0;
        }

        /// <summary>
        /// Convert to byte
        /// </summary>
        /// <param name="obj">json obj</param>
        /// <returns>byte</returns>
        public static implicit operator byte(JsonObject obj)
        {
            byte res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            byte.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to char
        /// </summary>
        /// <param name="obj">json obj</param>
        /// <returns>char</returns>
        public static implicit operator char(JsonObject obj)
        {
            char res = '\0';
            if (obj == null || obj.m_obj == null)
                return res;
            char.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to DateTime
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator DateTime(JsonObject obj)
        {
            DateTime res = DateTime.MinValue;
            if (obj == null || obj.m_obj == null)
                return res;
            DateTime.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to decimal
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator decimal(JsonObject obj)
        {
            decimal res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            decimal.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to double
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator double(JsonObject obj)
        {
            double res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            double.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to short
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator short(JsonObject obj)
        {
            short res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            short.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to int
        /// </summary>
        /// <param name="obj">json obj</param>
        /// <returns>int</returns>
        public static implicit operator int(JsonObject obj)
        {
            int res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            int.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to long
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator long(JsonObject obj)
        {
            long res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            long.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to sbyte
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator sbyte(JsonObject obj)
        {
            sbyte res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            sbyte.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to float
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator float(JsonObject obj)
        {
            float res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            float.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to String
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator string(JsonObject obj)
        {
            if (obj == null || obj.m_obj == null)
                return null;
            return obj.m_obj.ToString();
        }

        /// <summary>
        /// Convert to ushort
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator ushort(JsonObject obj)
        {
            ushort res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            ushort.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to uint
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator uint(JsonObject obj)
        {
            uint res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            uint.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }

        /// <summary>
        /// Convert to ulong
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator ulong(JsonObject obj)
        {
            ulong res = 0;
            if (obj == null || obj.m_obj == null)
                return res;
            ulong.TryParse(obj.m_obj.ToString(), out res);
            return res;
        }
        #endregion

        #region Convert to Simple Data(NullAble)

        /// <summary>
        /// Convert to bool
        /// </summary>
        /// <param name="obj">json obj(string "true" or !0 return true, others return false)</param>
        /// <returns>bool</returns>
        public static implicit operator bool?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (bool?)(bool)obj;
        }

        /// <summary>
        /// Convert to byte
        /// </summary>
        /// <param name="obj">json obj</param>
        /// <returns>byte</returns>
        public static implicit operator byte?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (byte?)(byte)obj;
        }

        /// <summary>
        /// Convert to char
        /// </summary>
        /// <param name="obj">json obj</param>
        /// <returns>char</returns>
        public static implicit operator char?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (char?)(char)obj;
        }

        /// <summary>
        /// Convert to DateTime
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator DateTime?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (DateTime?)(DateTime)obj;
        }

        /// <summary>
        /// Convert to decimal
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator decimal?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (decimal?)(decimal)obj;
        }

        /// <summary>
        /// Convert to double
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator double?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (double?)(double)obj;
        }

        /// <summary>
        /// Convert to short
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator short?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (short?)(short)obj;
        }

        /// <summary>
        /// Convert to int
        /// </summary>
        /// <param name="obj">json obj</param>
        /// <returns>int</returns>
        public static implicit operator int?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (int?)(int)obj;
        }

        /// <summary>
        /// Convert to long
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator long?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (long?)(long)obj;
        }

        /// <summary>
        /// Convert to sbyte
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator sbyte?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (sbyte?)(sbyte)obj;
        }

        /// <summary>
        /// Convert to float
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator float?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (float?)(float)obj;
        }

        /// <summary>
        /// Convert to ushort
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator ushort?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (ushort?)(ushort)obj;
        }

        /// <summary>
        /// Convert to uint
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator uint?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (uint?)(uint)obj;
        }

        /// <summary>
        /// Convert to ulong
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator ulong?(JsonObject obj)
        {
            return obj == null || obj.m_obj == null ? null : (ulong?)(ulong)obj;
        }
        #endregion

        #region Object Converter
        public static JsonObject FromObject(object o)
        {
            JsonObject jo = null;
            if (o == null)
                jo = new JsonObject();
            else if (o is bool || o is bool?)
                jo = (bool)o;
            else if (o is byte || o is byte?)
                jo = (byte)o;
            else if (o is char || o is char?)
                jo = (char)o;
            else if (o is DateTime || o is DateTime?)
                jo = (DateTime)o;
            else if (o is decimal || o is decimal?)
                jo = (decimal)o;
            else if (o is double || o is double?)
                jo = (double)o;
            else if (o is short || o is short?)
                jo = (short)o;
            else if (o is int || o is int?)
                jo = (int)o;
            else if (o is long || o is long?)
                jo = (long)o;
            else if (o is sbyte || o is sbyte?)
                jo = (sbyte)o;
            else if (o is string)
                jo = (string)o;
            else if (o is ushort || o is ushort?)
                jo = (ushort)o;
            else if (o is uint || o is uint?)
                jo = (uint)o;
            else if (o is ulong || o is ulong?)
                jo = (ulong)o;
            else if (o is IJsonClass)
                jo = JsonObject.FromJCO((IJsonClass)o);
            else
                jo = new JsonObject();
            return jo;
        }
        #endregion

        #region JsonHashTable & JsonList
        /// <summary>
        /// Convert to JsonHashTable
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator JsonHashTable(JsonObject obj)
        {
            return obj != null && obj.m_obj != null && obj.m_obj is JsonHashTable ? (JsonHashTable)obj.m_obj : null;
        }

        /// <summary>
        /// Convert to JsonList
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator JsonList(JsonObject obj)
        {
            return obj != null && obj.m_obj != null && obj.m_obj is JsonList ? (JsonList)obj.m_obj : null;
        }
        #endregion

        #region Array Or IList to JsonObject
        public static JsonObject FromArrayOrIList(IEnumerable<bool> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<bool> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<byte> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<byte> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<char> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<char> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<DateTime> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<DateTime> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<decimal> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<decimal> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<double> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<double> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<short> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<short> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<int> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<int> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<long> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<long> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<sbyte> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<sbyte> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<float> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<float> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<string> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<string> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<ushort> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<ushort> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<uint> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<uint> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }

        public static JsonObject FromArrayOrIList(IEnumerable<ulong> ienum)
        {
            JsonList list = new JsonList();
            IEnumerator<ulong> et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(et.Current);
            return list;
        }
        #endregion

        #region JsonObject to Array
        public static implicit operator bool[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            bool[] arr = new bool[list.Count];
            for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator byte[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            byte[] arr = new byte[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator char[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            char[] arr = new char[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator DateTime[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            DateTime[] arr = new DateTime[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator decimal[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            decimal[] arr = new decimal[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator double[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            double[] arr = new double[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator short[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            short[] arr = new short[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator int[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            int[] arr = new int[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator long[](JsonObject obj)
        {
            JsonList list = obj;
            if(list == null)
                return null;
            long[] arr = new long[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator sbyte[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            sbyte[] arr = new sbyte[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator float[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            float[] arr = new float[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator string[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            string[] arr = new string[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator ushort[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            ushort[] arr = new ushort[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator uint[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            uint[] arr = new uint[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }

        public static implicit operator ulong[](JsonObject obj)
        {
            JsonList list = obj;
            if (list == null)
                return null;
            ulong[] arr = new ulong[list.Count];
             for(int i = 0; i < list.Count; i++)
                arr[i] = list[i];
            return arr;
        }
        #endregion

        #region JsonObject to IList
        public void ToIList(IList<bool> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<byte> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<char> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<DateTime> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<decimal> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<double> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<short> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<int> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }
        public void ToIList(IList<long> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<sbyte> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<float> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<string> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<ushort> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<uint> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }

        public void ToIList(IList<ulong> ilist)
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                ilist.Add(o);
        }
        
        #endregion

        #region IJsonClass Converter
        /// <summary>
        /// Convert Json Class to jsonObject
        /// </summary>
        /// <param name="ijson"></param>
        /// <returns></returns>
        public static JsonObject FromJCO(IJsonClass ijson)
        {
            JsonHashTable hash = new JsonHashTable();
            ijson.JsonSerializeObj(hash);
            return hash;
        }

        public static JsonObject FromIJCOArrayOrList(IEnumerable ienum)
        {
            JsonList list = new JsonList();
            IEnumerator et = ienum.GetEnumerator();
            while (et.MoveNext())
                list.Add(JsonObject.FromJCO((IJsonClass)et.Current));
            return list;
        }

        /// <summary>
        /// Convert jsonObject to Json Class
        /// </summary>
        /// <param nae="ijson"></param>
        public T ToJCO<T>() where T : IJsonClass, new()
        {
            T ijson = new T();
            ijson.JsonDeSerializeObj(this);
            return ijson;
        }

        public T[] ToJCOArray<T>() where T : IJsonClass, new()
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return null;
            T[] arr = new T[list.Count];
            for (int i = 0; i < list.Count; i++)
                arr[i] = list[i].ToJCO<T>();
            return arr;
        }

        public void ToJCOIList<T>(IList<T> jcoList) where T:IJsonClass, new()
        {
            JsonList list = (JsonList)this;
            if (list == null)
                return;
            foreach (JsonObject o in list)
                jcoList.Add(o.ToJCO<T>());
        }

        #endregion

        #region DataTable Converter
        public static implicit operator JsonObject(DataTable dt)
        {
            JsonHashTable dtHash = new JsonHashTable();
            //save cols
            string[] cols = new string[dt.Columns.Count];
            for (int i = 0; i < dt.Columns.Count; i++)
                cols[i] = dt.Columns[i].ColumnName;
            dtHash["Columns"] = JsonObject.FromArrayOrIList(cols);

            //save rows
            JsonList rowList = new JsonList();
            foreach (DataRow row in dt.Rows)
            {
                JsonHashTable hash = new JsonHashTable();
                foreach (DataColumn col in dt.Columns)
                    hash[col.ColumnName] = JsonObject.FromObject(row[col.ColumnName]);
                rowList.Add(hash);
            }
            dtHash["Rows"] = rowList;
            return dtHash;
        }

        public static implicit operator DataTable(JsonObject obj)
        {
            JsonHashTable dtHash = obj;
            if (dtHash == null)
                return null;
            DataTable dt = new DataTable();
            
            //get columns
            foreach (string col in (string[])dtHash["Columns"])
                dt.Columns.Add(col);

            //rows
            foreach (JsonHashTable hash in (JsonList)dtHash["Rows"])
            {
                DataRow row = dt.NewRow();
                foreach (KeyValuePair<string, JsonObject> pos in hash)
                    row[pos.Key] = pos.Value;
            }   
            return dt;
        }
        #endregion
    }

    public class JsonHashTable : Dictionary<string, JsonObject> 
    { 
        public new JsonObject this[string key]
        {
            get
            {
                return base.ContainsKey(key) ? base[key] : null;
            }
            set
            {
                base[key] = value;
            }
        }
    };

    public class JsonList : List<JsonObject> { };
}


namespace Shc.Json
{

    /// <summary>
    /// Json Serializer Interface
    /// </summary>
    public interface IJsonClass
    {
        void JsonDeSerializeObj(JsonHashTable hashToRead);
        void JsonSerializeObj(JsonHashTable hashToFill);
    }

    #region JSON Main Class
    /// <summary>
    /// This class encodes and decodes JSON strings.
    /// 
    /// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
    /// All numbers are parsed to doubles.
    /// </summary>
    public class JSON
    {
        public const int TOKEN_NONE = 0;
        public const int TOKEN_CURLY_OPEN = 1;
        public const int TOKEN_CURLY_CLOSE = 2;
        public const int TOKEN_SQUARED_OPEN = 3;
        public const int TOKEN_SQUARED_CLOSE = 4;
        public const int TOKEN_COLON = 5;
        public const int TOKEN_COMMA = 6;
        public const int TOKEN_STRING = 7;
        public const int TOKEN_NUMBER = 8;
        public const int TOKEN_TRUE = 9;
        public const int TOKEN_FALSE = 10;
        public const int TOKEN_NULL = 11;

        private const int BUILDER_CAPACITY = 2000;

        protected static JSON instance = new JSON();

        #region JSON Decode

        /// <summary>
        /// On decoding, this value holds the position at which the parse failed (-1 = no error).
        /// </summary>
        protected int lastErrorIndex = -1;
        protected string lastDecode = "";

        /// <summary>
        /// Parses the string json into a value
        /// </summary>
        /// <param name="json">A JSON string.</param>
        /// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
        public static JsonObject JsonDecode(string json)
        {
            // save the string for debug information
            JSON.instance.lastDecode = json;

            if (json != null)
            {
                char[] charArray = json.ToCharArray();
                int index = 0;
                bool success = true;
                JsonObject value = JSON.instance.ParseValue(charArray, ref index, ref success);
                if (success)
                {
                    JSON.instance.lastErrorIndex = -1;
                }
                else
                {
                    JSON.instance.lastErrorIndex = index;
                }
                return value;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Parses the string json into a value
        /// </summary>
        /// <param name="json">A JSON string which escaped.</param>
        /// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
        public static JsonObject JsonUnEscapeDecode(string jsonEscaped)
        {
            return JsonDecode(UnEscape(jsonEscaped));
        }

        //public static bool JsonDecode(IJsonClass jsonClass, string json)
        //{
        //    JsonHashTable ht = JsonDecode(json);
        //    return ht == null ? false : JsonDecode(jsonClass, ht);
        //}

        //private static void JsonDecode(IJsonClass jsonClass, JsonHashTable hashTable)
        //{
        //    return jsonClass.JsonDeSerializeObj(hashTable);
        //}

       
        /// <summary>
        /// On decoding, this function returns the position at which the parse failed (-1 = no error).
        /// </summary>
        /// <returns></returns>
        public static bool LastDecodeSuccessful()
        {
            return (JSON.instance.lastErrorIndex == -1);
        }

        /// <summary>
        /// On decoding, this function returns the position at which the parse failed (-1 = no error).
        /// </summary>
        /// <returns></returns>
        public static int GetLastErrorIndex()
        {
            return JSON.instance.lastErrorIndex;
        }

        /// <summary>
        /// If a decoding error occurred, this function returns a piece of the JSON string 
        /// at which the error took place. To ease debugging.
        /// </summary>
        /// <returns></returns>
        public static string GetLastErrorSnippet()
        {
            if (JSON.instance.lastErrorIndex == -1)
            {
                return "";
            }
            else
            {
                int startIndex = JSON.instance.lastErrorIndex - 5;
                int endIndex = JSON.instance.lastErrorIndex + 15;
                if (startIndex < 0)
                {
                    startIndex = 0;
                }
                if (endIndex >= JSON.instance.lastDecode.Length)
                {
                    endIndex = JSON.instance.lastDecode.Length - 1;
                }

                return JSON.instance.lastDecode.Substring(startIndex, endIndex - startIndex + 1);
            }
        }

        protected JsonObject ParseObject(char[] json, ref int index)
        {
            JsonHashTable table = new JsonHashTable();
            int token;

            // {
            NextToken(json, ref index);

            bool done = false;
            while (!done)
            {
                token = LookAhead(json, index);
                if (token == JSON.TOKEN_NONE)
                {
                    return null;
                }
                else if (token == JSON.TOKEN_COMMA)
                {
                    NextToken(json, ref index);
                }
                else if (token == JSON.TOKEN_CURLY_CLOSE)
                {
                    NextToken(json, ref index);
                    return table;
                }
                else
                {

                    // name
                    string name = ParseString(json, ref index);
                    if (name == null)
                    {
                        return null;
                    }

                    // :
                    token = NextToken(json, ref index);
                    if (token != JSON.TOKEN_COLON)
                    {
                        return null;
                    }

                    // value
                    bool success = true;
                    JsonObject value = ParseValue(json, ref index, ref success);
                    if (!success)
                    {
                        return null;
                    }

                    table[name] = value;
                }
            }

            return table;
        }

        protected JsonObject ParseArray(char[] json, ref int index)
        {
            JsonList array = new JsonList();

            // [
            NextToken(json, ref index);

            bool done = false;
            while (!done)
            {
                int token = LookAhead(json, index);
                if (token == JSON.TOKEN_NONE)
                {
                    return null;
                }
                else if (token == JSON.TOKEN_COMMA)
                {
                    NextToken(json, ref index);
                }
                else if (token == JSON.TOKEN_SQUARED_CLOSE)
                {
                    NextToken(json, ref index);
                    break;
                }
                else
                {
                    bool success = true;
                    JsonObject value = ParseValue(json, ref index, ref success);
                    if (!success)
                    {
                        return null;
                    }

                    array.Add(value);
                }
            }

            return array;
        }

        protected JsonObject ParseValue(char[] json, ref int index, ref bool success)
        {
            switch (LookAhead(json, index))
            {
                case JSON.TOKEN_STRING:
                    return ParseString(json, ref index);
                case JSON.TOKEN_NUMBER:
                    return ParseNumber(json, ref index);
                case JSON.TOKEN_CURLY_OPEN:
                    return ParseObject(json, ref index);
                case JSON.TOKEN_SQUARED_OPEN:
                    return ParseArray(json, ref index);
                case JSON.TOKEN_TRUE:
                    NextToken(json, ref index);
                    return 1;
                case JSON.TOKEN_FALSE:
                    NextToken(json, ref index);
                    return 0;
                case JSON.TOKEN_NULL:
                    NextToken(json, ref index);
                    return null;
                case JSON.TOKEN_NONE:
                    break;
            }

            success = false;
            return null;
        }

        protected JsonObject ParseString(char[] json, ref int index)
        {
            string s = "";
            char c;

            EatWhitespace(json, ref index);

            // "
            c = json[index++];

            bool complete = false;
            while (!complete)
            {

                if (index == json.Length)
                {
                    break;
                }

                c = json[index++];
                if (c == '"')
                {
                    complete = true;
                    break;
                }
                else if (c == '\\')
                {

                    if (index == json.Length)
                    {
                        break;
                    }
                    c = json[index++];
                    if (c == '"')
                    {
                        s += '"';
                    }
                    else if (c == '\\')
                    {
                        s += '\\';
                    }
                    else if (c == '/')
                    {
                        s += '/';
                    }
                    else if (c == 'b')
                    {
                        s += '\b';
                    }
                    else if (c == 'f')
                    {
                        s += '\f';
                    }
                    else if (c == 'n')
                    {
                        s += '\n';
                    }
                    else if (c == 'r')
                    {
                        s += '\r';
                    }
                    else if (c == 't')
                    {
                        s += '\t';
                    }
                    else if (c == 'u')
                    {
                        int remainingLength = json.Length - index;
                        if (remainingLength >= 4)
                        {
                            // fetch the next 4 chars
                            char[] unicodeCharArray = new char[4];
                            Array.Copy(json, index, unicodeCharArray, 0, 4);
                            // parse the 32 bit hex into an integer codepoint
                            uint codePoint = UInt32.Parse(new string(unicodeCharArray), NumberStyles.HexNumber);
                            // convert the integer codepoint to a unicode char and add to string
                            s += Char.ConvertFromUtf32((int)codePoint);
                            // skip 4 chars
                            index += 4;
                        }
                        else
                        {
                            break;
                        }
                    }

                }
                else
                {
                    s += c;
                }

            }

            if (!complete)
            {
                return null;
            }

            return s;
        }

        protected JsonObject ParseNumber(char[] json, ref int index)
        {
            EatWhitespace(json, ref index);

            int lastIndex = GetLastIndexOfNumber(json, index);
            int charLength = (lastIndex - index) + 1;
            char[] numberCharArray = new char[charLength];

            Array.Copy(json, index, numberCharArray, 0, charLength);
            index = lastIndex + 1;
            return new string(numberCharArray);
        }

        protected int GetLastIndexOfNumber(char[] json, int index)
        {
            int lastIndex;
            for (lastIndex = index; lastIndex < json.Length; lastIndex++)
            {
                if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1)
                {
                    break;
                }
            }
            return lastIndex - 1;
        }

        protected void EatWhitespace(char[] json, ref int index)
        {
            for (; index < json.Length; index++)
            {
                if (" \t\n\r".IndexOf(json[index]) == -1)
                {
                    break;
                }
            }
        }

        protected int LookAhead(char[] json, int index)
        {
            int saveIndex = index;
            return NextToken(json, ref saveIndex);
        }

        protected int NextToken(char[] json, ref int index)
        {
            EatWhitespace(json, ref index);

            if (index == json.Length)
            {
                return JSON.TOKEN_NONE;
            }

            char c = json[index];
            index++;
            switch (c)
            {
                case '{':
                    return JSON.TOKEN_CURLY_OPEN;
                case '}':
                    return JSON.TOKEN_CURLY_CLOSE;
                case '[':
                    return JSON.TOKEN_SQUARED_OPEN;
                case ']':
                    return JSON.TOKEN_SQUARED_CLOSE;
                case ',':
                    return JSON.TOKEN_COMMA;
                case '"':
                    return JSON.TOKEN_STRING;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '-':
                    return JSON.TOKEN_NUMBER;
                case ':':
                    return JSON.TOKEN_COLON;
            }
            index--;

            int remainingLength = json.Length - index;

            // false
            if (remainingLength >= 5)
            {
                if (json[index] == 'f' &&
                    json[index + 1] == 'a' &&
                    json[index + 2] == 'l' &&
                    json[index + 3] == 's' &&
                    json[index + 4] == 'e')
                {
                    index += 5;
                    return JSON.TOKEN_FALSE;
                }
            }

            // true
            if (remainingLength >= 4)
            {
                if (json[index] == 't' &&
                    json[index + 1] == 'r' &&
                    json[index + 2] == 'u' &&
                    json[index + 3] == 'e')
                {
                    index += 4;
                    return JSON.TOKEN_TRUE;
                }
            }

            // null
            if (remainingLength >= 4)
            {
                if (json[index] == 'n' &&
                    json[index + 1] == 'u' &&
                    json[index + 2] == 'l' &&
                    json[index + 3] == 'l')
                {
                    index += 4;
                    return JSON.TOKEN_NULL;
                }
            }

            return JSON.TOKEN_NONE;
        }

        #endregion

        #region Json Encode

        /// <summary>
        /// Converts a Hashtable / ArrayList object into a JSON string
        /// </summary>
        /// <param name="json">A Hashtable / ArrayList</param>
        /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
        public static string JsonEncode(JsonObject json)
        {
            StringBuilder builder = new StringBuilder(BUILDER_CAPACITY);
            JSON.instance.SerializeValue(json, builder);
            return builder.ToString();
        }

        /// <summary>
        /// Converts a Hashtable / ArrayList object into a ESCAPE JSON string
        /// </summary>
        /// <param name="json">A Hashtable / ArrayList</param>
        /// <returns>A ESCAPE JSON encoded string, or null if object 'json' is not serializable</returns>
        public static string JsonEscapeEncode(JsonObject json)
        {
            return Escape(JsonEncode(json));
        }

        //public static string JsonEncode(IJsonClass jsonClass)
        //{
        //    return JsonEncode(jsonClass.JsonSerializeObj());
        //}

        protected void SerializeObject(JsonHashTable anObject, StringBuilder builder)
        {
            builder.Append("{");
            bool first = true;
            foreach (KeyValuePair<string, JsonObject> e in anObject)
            {
                if (!first)
                {
                    builder.Append(", ");
                }

                SerializeString(e.Key, builder);
                builder.Append(":");
                SerializeValue(e.Value, builder);

                first = false;
            }
            builder.Append("}");
        }

        protected void SerializeArray(JsonList anArray, StringBuilder builder)
        {
            builder.Append("[");

            bool first = true;
            for (int i = 0; i < anArray.Count; i++)
            {
                JsonObject value = anArray[i];

                if (!first)
                {
                    builder.Append(", ");
                }

                SerializeValue(value, builder);

                first = false;
            }

            builder.Append("]");
        }

        protected void SerializeValue(JsonObject value, StringBuilder builder)
        {
            if (value == null || value.IsNull())
            {
                builder.Append("null");
            }
            else if (value.IsString())
            {
                SerializeString(value, builder);
            }
            else if (value.IsHashTable())
            {
                SerializeObject(value, builder);
            }
            else if (value.IsArrayList())
            {
                SerializeArray(value, builder);
            }
            else if (value.IsDateTime())
            {
                SerializeString(((DateTime)value).ToString(), builder);
            }
            else if (value.IsBool())
            {
                builder.Append(value ? "1" : "0");
            }
            else
            {
                SerializeNumber(value, builder);
            }
        }

        protected void SerializeString(string aString, StringBuilder builder)
        {
            builder.Append("\"");

            char[] charArray = aString.ToCharArray();
            for (int i = 0; i < charArray.Length; i++)
            {
                char c = charArray[i];
                if (c == '"')
                {
                    builder.Append("\\\"");
                }
                else if (c == '\\')
                {
                    builder.Append("\\\\");
                }
                else if (c == '\b')
                {
                    builder.Append("\\b");
                }
                else if (c == '\f')
                {
                    builder.Append("\\f");
                }
                else if (c == '\n')
                {
                    builder.Append("\\n");
                }
                else if (c == '\r')
                {
                    builder.Append("\\r");
                }
                else if (c == '\t')
                {
                    builder.Append("\\t");
                }
                else
                {
                    int codepoint = Convert.ToInt32(c);
                    if ((codepoint >= 32) && (codepoint <= 126))
                    {
                        builder.Append(c);
                    }
                    else
                    {
                        builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0'));
                    }
                }
            }

            builder.Append("\"");
        }

        protected void SerializeNumber(double number, StringBuilder builder)
        {
            builder.Append(Convert.ToString(number, CultureInfo.InvariantCulture));
        }
        #endregion

        #region escape, unescape
        public static string Escape(string str)
        {
            string hexTable = "0123456789abcdef";
            StringBuilder strb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
                     || c == '@' || c == '*' || c == '-' || c == '_' || c == '+' || c == '.' || c == '/')
                    strb.Append(c);
                else if (c <= 255)
                {
                    strb.Append('%');
                    strb.Append(hexTable[c >> 4]);
                    strb.Append(hexTable[c & 0x0f]);
                }
                else
                {
                    strb.Append("%u");
                    strb.Append(hexTable[c >> 12]);
                    strb.Append(hexTable[(c & 0x0fff) >> 8]);
                    strb.Append(hexTable[(c & 0x00ff) >> 4]);
                    strb.Append(hexTable[c & 0x000f]);
                }
            }
            return strb.ToString();
        }

        static char _unEscapeHex2Char(string hexStr)
        {
            int val = 0;
            foreach (char c in hexStr)
            {
                if (c >= '0' && c <= '9')
                    val = (val << 4) + (c - '0');
                else if (c >= 'a' && c <= 'f')
                    val = (val << 4) + (c - 'a' + 10);
                else if (c >= 'A' && c <= 'F')
                    val = (val << 4) + (c - 'A' + 10);
                else
                    return (char)0;
            }
            return (char)val;
        }

        static string _unEscapeReplaceFun(Match mres)
        {
            string val = mres.Value;
            int l = val.Length;
            if (l == 3)
                return "" + _unEscapeHex2Char(val.Substring(1));
            else if (l == 6)
                return "" + _unEscapeHex2Char(val.Substring(2));
            else
                return val;
        }

        public static string UnEscape(string str)
        {
            Regex reg = new Regex("(%(\\d|[a-f]|[A-F]){2,2})|(%(u|U)((\\d|[a-f]|[A-F]){4,4}))");
            return reg.Replace(str, new MatchEvaluator(_unEscapeReplaceFun));
        }
        #endregion
    }

    #endregion
}
